Array.prototype.remove = function (start, end) {
    this.splice(start, end);
    return this;
}

view.currentScroll = new Point(0, 0);
var scrollVector = new Point(0, 0);
var scrollMargin = 32;

var imgWidth = $('.puzzle-image').css('width').replace('px', '');
var imgHeight = $('.puzzle-image').css('height').replace('px', '');

var config = ({
    imgName: 'puzzle-image',
    // bcgImgName: 'background-image',
    tileWidth: 64,
    imgWidth: imgWidth,
    imgHeight: imgHeight
});

var shadow = new Raster(config.imgName);
shadow.position = view.center
shadow.opacity = 0;

// var background = new Raster(config.bcgImgName);
// background.position = view.center;
// background.opacity = 0.7;
// background.size = view.viewSize;

var puzzle = new Jigsaw(config, game_mode);


$('.puzzle-image').css('margin', '-' + imgHeight / 2 + 'px 0 0 -' + imgWidth / 2 + 'px');

function onMouseDown(event) {
    puzzle.pickTile(event.point);
}

function onMouseUp(event) {
    if (hint == 1) {
        hint = 0;
        for (var i = 0; i < puzzle.hintA.length; i++) {
            puzzle.tiles[puzzle.hintA[i]].opacity = 1;
        }
        for (var i = 0; i < puzzle.hintB.length; i++) {
            puzzle.tiles[puzzle.hintB[i]].opacity = 1;
        }
        puzzle.hintA = undefined;
        puzzle.hintB = undefined;
    }
    puzzle.releaseTile();
}

function onMouseDrag(event) {
    if (game_mode == 1) {
        puzzle.dragTileQuick(event.delta);
    }
    else {
        puzzle.dragTile(event.delta);
    }// 改成根据全局变量判断移动逻辑
}

function onMouseMove() {
}

function onFrame() {
    if (isCounting) {
        if (shadowShow == 1) {
            shadow.opacity = 0.45;
        } else {
            shadow.opacity = 0;
        }
        if (puzzle.borderOnly != borderOnly) {
            puzzle.changeBorderShow(1 - borderOnly);
            puzzle.borderOnly = borderOnly;
        }
        if (hint == 1) {
            if (!puzzle.hintA || !puzzle.hintB) {
                if (!puzzle.getHint()) {
                    return;
                }
                puzzle.hintCnt = 1;
            }
            if (puzzle.tiles[puzzle.hintA[0]].opacity <= 0.5) {
                puzzle.hintChange = 1;
                if (puzzle.hintCnt < 10) {
                    puzzle.hintCnt++;
                }
            }
            else if (puzzle.tiles[puzzle.hintA[0]].opacity >= 1) {
                puzzle.hintChange = -1;
            }
            for (var i = 0; i < puzzle.hintA.length; i++) {
                puzzle.tiles[puzzle.hintA[i]].opacity += puzzle.hintChange * puzzle.hintCnt * 0.01;
            }
            for (var i = 0; i < puzzle.hintB.length; i++) {
                puzzle.tiles[puzzle.hintB[i]].opacity += puzzle.hintChange * puzzle.hintCnt * 0.01;
            }
        }
        else if (puzzle.hintA != undefined || puzzle.hintB != undefined) {
            for (var i = 0; i < puzzle.hintA.length; i++) {
                puzzle.tiles[puzzle.hintA[i]].opacity = 1;
            }
            for (var i = 0; i < puzzle.hintB.length; i++) {
                puzzle.tiles[puzzle.hintB[i]].opacity = 1;
            }
            puzzle.hintA = undefined;
            puzzle.hintB = undefined;
        }
        if (repos == 1) {
            puzzle.rearrange();
            repos = 0;
        }
    }
}

function onResize() {
    var x = view.viewSize.width / 2, y = view.viewSize.height / 2;
    var shadowDelta = new Point(x - shadow.position.x, y - shadow.position.y);
    puzzle.mapPostions();
    shadow.translate(shadowDelta);
};

function Jigsaw(config, mode) {
    instance = this;
    this.imgName = config.imgName;
    this.puzzleImage = new Raster(config.imgName);
    this.puzzleImage.position = view.center;
    this.puzzleImage.opacity = 0;
    this.tileWidth = config.tileWidth;
    this.tilesPerRow = Math.floor(config.imgWidth / config.tileWidth);
    this.tilesPerColumn = Math.floor(config.imgHeight / config.tileWidth);
    this.tileMarginWidth = this.tileWidth * 0.203125;
    this.selectedTile = undefined;
    this.selectedTileIndex = undefined;
    this.viewWidth = view.viewSize.width;
    this.viewHeight = view.viewSize.height;
    resizeImage();
    this.tiles = createTiles(this.tilesPerRow, this.tilesPerColumn);
    this.mode = mode;
    this.borderOnly = 0; // show border only
    this.hintA = undefined;
    this.hintB = undefined;
    this.hintChange = -1; // -1 decrease, 1 increase
    this.hintCnt = 1;

    function resizeImage() {
        var difRatio = 1; // change this variable according to difficulty

        var numTilesEasy = 30;
        var numTilesMedium = 60;
        var numTilesHard = 110;

        if (difficulty == 1) { // medium
            difRatio = Math.sqrt(numTilesMedium / (instance.puzzleImage.width * instance.puzzleImage.height / instance.tileWidth / instance.tileWidth));
        }
        else if (difficulty == 2) { // hard
            difRatio = Math.sqrt(numTilesHard / (instance.puzzleImage.width * instance.puzzleImage.height / instance.tileWidth / instance.tileWidth));
        }
        else { // easy
            difRatio = Math.sqrt(numTilesEasy / (instance.puzzleImage.width * instance.puzzleImage.height / instance.tileWidth / instance.tileWidth));
        }

        instance.puzzleImage.size = new Size(difRatio * instance.puzzleImage.width, difRatio * instance.puzzleImage.height);
        instance.tilesPerRow = Math.floor(instance.puzzleImage.width / instance.tileWidth);
        instance.tilesPerColumn = Math.floor(instance.puzzleImage.height / instance.tileWidth);
        shadow.size = new Size(difRatio * shadow.width, difRatio * shadow.height);

        var desiredWidth = instance.tilesPerRow * instance.tileWidth;
        var desiredHeight = instance.tilesPerColumn * instance.tileWidth;
        instance.puzzleImage = new Raster(instance.puzzleImage.getSubImage(new Rectangle(0, 0, desiredWidth, desiredHeight)));
        instance.puzzleImage.position = view.center;
        instance.puzzleImage.opacity = 0;
        shadow = new Raster(shadow.getSubImage(new Rectangle(0, 0, desiredWidth, desiredHeight)));
        shadow.position = view.center
        shadow.opacity = 0;
    }

    function createTiles(xTileCount, yTileCount) {
        var totalSize = instance.viewWidth * instance.viewHeight;
        var totalTileSize = instance.tilesPerColumn * instance.tilesPerRow * instance.tileWidth * instance.tileWidth;
        var ratio = Math.sqrt(0.3 * totalSize / totalTileSize);

        var tiles = new Array();
        var tileRatio = instance.tileWidth / 100.0;

        var shapeArray = getRandomShapes(xTileCount, yTileCount);
        var tileIndexes = new Array();
        for (var y = 0; y < yTileCount; y++) {
            for (var x = 0; x < xTileCount; x++) {

                var shape = shapeArray[y * xTileCount + x];

                var mask = getMask(tileRatio, shape.topTab, shape.rightTab, shape.bottomTab, shape.leftTab, instance.tileWidth);
                mask.opacity = 0.25;
                mask.strokeColor = '#fff';

                var cloneImg = instance.puzzleImage.clone();
                var img = getTileRaster(
                    cloneImg,
                    new Size(instance.tileWidth, instance.tileWidth),
                    new Point(instance.tileWidth * x, instance.tileWidth * y)
                );

                var tile = new Group(mask, img);
                tile.clipped = true;

                tile.shape = shape;
                tile.imagePosition = new Point(x, y);

                tile.depth = y * xTileCount + x; // depth
                tile.root = y * xTileCount + x; // the root of "union-find" (not really)
                tile.opacity = 1;

                tile.scale(ratio);

                tiles.push(tile);
                tileIndexes.push(tileIndexes.length);
            }
        }

        instance.tileWidth *= ratio;
        shadow.scale(ratio);

        for (var i = 0; i < tiles.length; i++) { // randomize initial position
            var index1 = Math.floor(Math.random() * tileIndexes.length);
            var index2 = tileIndexes[index1];
            var tile = tiles[index2];
            tileIndexes.remove(index1, 1);

            var randomX = Math.floor(Math.random() * (view.viewSize.width - instance.tileWidth) + instance.tileWidth / 2);
            var randomY = Math.floor(Math.random() * (view.viewSize.height - instance.tileWidth) + instance.tileWidth / 2);
            tile.position = new Point(randomX, randomY);
        }
        return tiles;
    }

    function getRandomShapes(width, height) {
        var shapeArray = new Array();

        for (var y = 0; y < height; y++) {
            for (var x = 0; x < width; x++) {

                var topTab = undefined;
                var rightTab = undefined;
                var bottomTab = undefined;
                var leftTab = undefined;

                if (y == 0)
                    topTab = 0;

                if (y == height - 1)
                    bottomTab = 0;

                if (x == 0)
                    leftTab = 0;

                if (x == width - 1)
                    rightTab = 0;

                shapeArray.push(
                    ({
                        topTab: topTab,
                        rightTab: rightTab,
                        bottomTab: bottomTab,
                        leftTab: leftTab
                    })
                );
            }
        }

        for (var y = 0; y < height; y++) {
            for (var x = 0; x < width; x++) {

                var shape = shapeArray[y * width + x];

                var shapeRight = (x < width - 1) ?
                    shapeArray[y * width + (x + 1)] :
                    undefined;

                var shapeBottom = (y < height - 1) ?
                    shapeArray[(y + 1) * width + x] :
                    undefined;

                shape.rightTab = (x < width - 1) ?
                    getRandomTabValue() :
                    shape.rightTab;

                if (shapeRight)
                    shapeRight.leftTab = - shape.rightTab;

                shape.bottomTab = (y < height - 1) ?
                    getRandomTabValue() :
                    shape.bottomTab;

                if (shapeBottom)
                    shapeBottom.topTab = - shape.bottomTab;
            }
        }
        return shapeArray;
    }

    function getRandomTabValue() {
        return Math.pow(-1, Math.floor(Math.random() * 2));
    }

    function getMask(tileRatio, topTab, rightTab, bottomTab, leftTab, tileWidth) {

        var curvyCoords = [
            0, 0, 35, 15, 37, 5,
            37, 5, 40, 0, 38, -5,
            38, -5, 20, -20, 50, -20,
            50, -20, 80, -20, 62, -5,
            62, -5, 60, 0, 63, 5,
            63, 5, 65, 15, 100, 0
        ];

        var mask = new Path();

        var topLeftEdge = new Point(-4, 4);

        mask.moveTo(topLeftEdge);

        // Top
        for (var i = 0; i < curvyCoords.length / 6; i++) {
            var p1 = topLeftEdge + new Point(curvyCoords[i * 6 + 0] * tileRatio, topTab * curvyCoords[i * 6 + 1] * tileRatio);
            var p2 = topLeftEdge + new Point(curvyCoords[i * 6 + 2] * tileRatio, topTab * curvyCoords[i * 6 + 3] * tileRatio);
            var p3 = topLeftEdge + new Point(curvyCoords[i * 6 + 4] * tileRatio, topTab * curvyCoords[i * 6 + 5] * tileRatio);

            mask.cubicCurveTo(p1, p2, p3);
        }
        // Right
        var topRightEdge = topLeftEdge + new Point(tileWidth, 0);
        for (var i = 0; i < curvyCoords.length / 6; i++) {
            var p1 = topRightEdge + new Point(-rightTab * curvyCoords[i * 6 + 1] * tileRatio, curvyCoords[i * 6 + 0] * tileRatio);
            var p2 = topRightEdge + new Point(-rightTab * curvyCoords[i * 6 + 3] * tileRatio, curvyCoords[i * 6 + 2] * tileRatio);
            var p3 = topRightEdge + new Point(-rightTab * curvyCoords[i * 6 + 5] * tileRatio, curvyCoords[i * 6 + 4] * tileRatio);

            mask.cubicCurveTo(p1, p2, p3);
        }
        // Bottom
        var bottomRightEdge = topRightEdge + new Point(0, tileWidth);
        for (var i = 0; i < curvyCoords.length / 6; i++) {
            var p1 = bottomRightEdge - new Point(curvyCoords[i * 6 + 0] * tileRatio, bottomTab * curvyCoords[i * 6 + 1] * tileRatio);
            var p2 = bottomRightEdge - new Point(curvyCoords[i * 6 + 2] * tileRatio, bottomTab * curvyCoords[i * 6 + 3] * tileRatio);
            var p3 = bottomRightEdge - new Point(curvyCoords[i * 6 + 4] * tileRatio, bottomTab * curvyCoords[i * 6 + 5] * tileRatio);

            mask.cubicCurveTo(p1, p2, p3);
        }
        // Left
        var bottomLeftEdge = bottomRightEdge - new Point(tileWidth, 0);
        for (var i = 0; i < curvyCoords.length / 6; i++) {
            var p1 = bottomLeftEdge - new Point(-leftTab * curvyCoords[i * 6 + 1] * tileRatio, curvyCoords[i * 6 + 0] * tileRatio);
            var p2 = bottomLeftEdge - new Point(-leftTab * curvyCoords[i * 6 + 3] * tileRatio, curvyCoords[i * 6 + 2] * tileRatio);
            var p3 = bottomLeftEdge - new Point(-leftTab * curvyCoords[i * 6 + 5] * tileRatio, curvyCoords[i * 6 + 4] * tileRatio);

            mask.cubicCurveTo(p1, p2, p3);
        }

        return mask;
    }

    function getTileRaster(sourceRaster, size, offset) {
        var targetRaster = new Raster('empty');
        var tileWithMarginWidth = size.width + instance.tileMarginWidth * 2;
        var data = sourceRaster.getData(new Rectangle(
            offset.x - instance.tileMarginWidth,
            offset.y - instance.tileMarginWidth,
            tileWithMarginWidth,
            tileWithMarginWidth));
        targetRaster.setData(data, new Point(0, 0))
        targetRaster.position = new Point(28, 36);
        return targetRaster;
    }

    this.pickTile = function (point) {
        var tolerance = instance.tileWidth * .48;
        var topTile = null;
        var topIndex = null;
        for (var index = 0; index < instance.tiles.length; index++) {
            var tile = instance.tiles[index];
            var tileCenter = tile.position;
            var hit = false;

            if (tile.opacity < 0.1) continue; // neglect the invisible tiles
            var deltaPoint = tileCenter - point;
            hit = (deltaPoint.x * deltaPoint.x +
                deltaPoint.y * deltaPoint.y) < tolerance * tolerance;

            if (hit) {
                if (!topTile || topTile.depth > tile.depth) {
                    topTile = tile;
                    topIndex = index;
                }
            }
        }
        if (topTile) {
            instance.selectedTile = topTile;
            instance.selectedTileIndex = topIndex;
        }
        else {
            instance.selectedTile = null;
        }

        if (instance.selectedTile) {
            var tile = instance.selectedTile;
            var root = tile.root;
            for (var i = 0; i < instance.tiles.length; i++) {
                if (instance.tiles[i].depth < 100) {
                    instance.tiles[i].depth++;
                }
                if (instance.tiles[i].root == root) {
                    instance.tiles[i].depth = 0;
                    project.activeLayer.addChild(instance.tiles[i]);
                }
            }
            project.activeLayer.selected = false;
        }

    }

    this.dragTile = function (delta) {
        if (instance.selectedTile) {
            moveTiles(instance.selectedTile, delta);
        }
    }

    this.dragTileQuick = function (delta) {
        if (instance.selectedTile) {
            moveTiles(instance.selectedTile, delta);

            var root = instance.selectedTile.root;
            for (var i = 0; i < instance.tiles.length; i++) {
                var tile = instance.tiles[i];
                if (tile.root != root) {
                    continue;
                }
                var selectedX = i % instance.tilesPerRow;
                var selectedY = parseInt(i / instance.tilesPerRow);
                var tolerance = instance.tileWidth * 1.5;
                if (selectedX != 0) { // left
                    var leftTile = instance.tiles[i - 1];
                    var leftPosition = leftTile.position;
                    var deltaPoint = tile.position - leftPosition - new Point(instance.tileWidth, 0);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(leftTile, tile, deltaPoint);
                        leftTile.depth = 0;
                        project.activeLayer.addChild(leftTile);
                    }
                }
                if (selectedX != instance.tilesPerRow - 1) { // right
                    var rightTile = instance.tiles[i + 1];
                    var rightPosition = rightTile.position;
                    var deltaPoint = tile.position - rightPosition + new Point(instance.tileWidth, 0);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(rightTile, tile, deltaPoint);
                        rightTile.depth = 0;
                        project.activeLayer.addChild(rightTile);
                    }
                }
                if (selectedY != 0) { // top
                    var topTile = instance.tiles[i - instance.tilesPerRow];
                    var topPosition = topTile.position;
                    var deltaPoint = tile.position - topPosition - new Point(0, instance.tileWidth);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(topTile, tile, deltaPoint);
                        topTile.depth = 0;
                        project.activeLayer.addChild(topTile);
                    }
                }
                if (selectedY != instance.tilesPerColumn - 1) { // bottom
                    var bottomTile = instance.tiles[i + instance.tilesPerRow];
                    var bottomPosition = bottomTile.position;
                    var deltaPoint = tile.position - bottomPosition + new Point(0, instance.tileWidth);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(bottomTile, tile, deltaPoint);
                        bottomTile.depth = 0;
                        project.activeLayer.addChild(bottomTile);
                    }
                }
            }
            project.activeLayer.selected = false;
        }
    }

    this.releaseTile = function () {
        if (instance.selectedTile) {
            var pos = instance.selectedTile.position;
            var newPos = new Point(0, 0);
            if (pos.y < instance.tileWidth / 2) {
                newPos.y = instance.tileWidth / 2;
            }
            else if (pos.y > view.viewSize.height - instance.tileWidth / 2) {
                newPos.y = view.viewSize.height - instance.tileWidth / 2;
            }
            else {
                newPos.y = pos.y;
            }
            if (pos.x < instance.tileWidth / 2) {
                newPos.x = instance.tileWidth / 2;
            }
            else if (pos.x > view.viewSize.width - instance.tileWidth / 2) {
                newPos.x = view.viewSize.width - instance.tileWidth / 2;
            }
            else {
                newPos.x = pos.x;
            }
            moveTiles(instance.selectedTile, newPos - pos);

            var root = instance.selectedTile.root;
            for (var i = 0; i < instance.tiles.length; i++) {
                var tile = instance.tiles[i];
                if (tile.root != root) {
                    continue;
                }
                var selectedX = i % instance.tilesPerRow;
                var selectedY = parseInt(i / instance.tilesPerRow);
                var tolerance = instance.tileWidth * 0.4;
                if (selectedX != 0) { // left
                    var leftTile = instance.tiles[i - 1];
                    var leftPosition = leftTile.position;
                    var deltaPoint = tile.position - leftPosition - new Point(instance.tileWidth, 0);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(tile, leftTile, -deltaPoint);
                        leftTile.depth = 0;
                        project.activeLayer.addChild(leftTile);
                    }
                }
                if (selectedX != instance.tilesPerRow - 1) { // right
                    var rightTile = instance.tiles[i + 1];
                    var rightPosition = rightTile.position;
                    var deltaPoint = tile.position - rightPosition + new Point(instance.tileWidth, 0);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(tile, rightTile, -deltaPoint);
                        rightTile.depth = 0;
                        project.activeLayer.addChild(rightTile);
                    }
                }
                if (selectedY != 0) { // top
                    var topTile = instance.tiles[i - instance.tilesPerRow];
                    var topPosition = topTile.position;
                    var deltaPoint = tile.position - topPosition - new Point(0, instance.tileWidth);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(tile, topTile, -deltaPoint);
                        topTile.depth = 0;
                        project.activeLayer.addChild(topTile);
                    }
                }
                if (selectedY != instance.tilesPerColumn - 1) { // bottom
                    var bottomTile = instance.tiles[i + instance.tilesPerRow];
                    var bottomPosition = bottomTile.position;
                    var deltaPoint = tile.position - bottomPosition + new Point(0, instance.tileWidth);
                    if (deltaPoint.x * deltaPoint.x + deltaPoint.y * deltaPoint.y < tolerance * tolerance) {
                        unionTiles(tile, bottomTile, -deltaPoint);
                        bottomTile.depth = 0;
                        project.activeLayer.addChild(bottomTile);
                    }
                }
            }
            instance.selectedTile = null;
            project.activeLayer.selected = false;

            if (checkTiles() && isCounting) {
                endGame(); // the puzzle is solved
            }
        }
    }

    function moveTiles(tile, delta) { // move tiles that has the same root
        var root = tile.root;
        for (var i = 0; i < instance.tiles.length; i++) {
            if (instance.tiles[i].root == root) {
                instance.tiles[i].position += delta;
            }
        }
    }

    function unionTiles(tileA, tileB, delta) { // combine tiles, delta is the distance tileA needs to move
        if (tileA.root == tileB.root || tileA.opacity < 0.1 || tileB.opacity < 0.1) { // already combined or not visible
            return;
        }
        moveTiles(tileA, delta);
        var rootA = tileA.root;
        for (var i = 0; i < instance.tiles.length; i++) {
            if (instance.tiles[i].root == rootA) {
                instance.tiles[i].root = tileB.root;
            }
        }

    }

    function checkTiles() { // check if the puzzle is completed
        for (var i = 1; i < instance.tiles.length; i++) {
            if (instance.tiles[i].root != instance.tiles[i - 1].root) {
                return false;
            }
        }
        return true;
    }

    this.changeBorderShow = function (opc) {
        var flags = new Array(instance.tiles.length);
        for (var i = 0; i < instance.tilesPerColumn; i++) {
            if (!flags[i * instance.tilesPerRow + 0]) {
                var root = instance.tiles[i * instance.tilesPerRow + 0].root;
                for (var j = 0; j < instance.tiles.length; j++) {
                    if (instance.tiles[j].root == root) {
                        flags[j] = 1;
                    }
                }
            }
            if (!flags[i * instance.tilesPerRow + instance.tilesPerRow - 1]) {
                var root = instance.tiles[i * instance.tilesPerRow + instance.tilesPerRow - 1].root;
                for (var j = 0; j < instance.tiles.length; j++) {
                    if (instance.tiles[j].root == root) {
                        flags[j] = 1;
                    }
                }
            }
        }
        for (var i = 0; i < instance.tilesPerRow; i++) {
            if (!flags[0 * instance.tilesPerRow + i]) {
                var root = instance.tiles[0 * instance.tilesPerRow + i].root;
                for (var j = 0; j < instance.tiles.length; j++) {
                    if (instance.tiles[j].root == root) {
                        flags[j] = 1;
                    }
                }
            }
            if (!flags[(instance.tilesPerColumn - 1) * instance.tilesPerRow + i]) {
                var root = instance.tiles[(instance.tilesPerColumn - 1) * instance.tilesPerRow + i].root;
                for (var j = 0; j < instance.tiles.length; j++) {
                    if (instance.tiles[j].root == root) {
                        flags[j] = 1;
                    }
                }
            }
        }

        for (var i = 1; i < instance.tilesPerColumn - 1; i++) {
            for (var j = 1; j < instance.tilesPerRow - 1; j++) {
                if (flags[i * instance.tilesPerRow + j] && !opc) { // do not hide those connected to tiles at edge
                    continue;
                }
                instance.tiles[i * instance.tilesPerRow + j].opacity = opc;
            }
        }
    }

    this.mapPostions = function () {
        var ratio;
        var widthRatio = view.viewSize.width / instance.viewWidth;
        var heightRatio = view.viewSize.height / instance.viewHeight;
        ratio = Math.sqrt(widthRatio * heightRatio);
        for (var i = 0; i < instance.tiles.length; i++) {
            instance.tiles[i].scale(ratio);
        }
        instance.tileWidth *= ratio;
        shadow.scale(ratio);

        var flags = new Array(instance.tiles.length);
        for (var i = 0; i < instance.tiles.length; i++) {
            var pos = instance.tiles[i].position;
            if (pos.x < 0 || pos.x > instance.viewWidth || pos.y < 0 || pos.y > instance.viewHeight)
                continue;
            if (!flags[i]) {
                var newPos = new Point(0, 0);
                newPos.x = pos.x / instance.viewWidth * view.viewSize.width;
                newPos.y = pos.y / instance.viewHeight * view.viewSize.height;
                // moveTiles(instance.tiles[i], newPos - pos);
                instance.tiles[i].position += (newPos - pos);
                var root = instance.tiles[i].root;
                var xofi = i % instance.tilesPerRow;
                var yofi = parseInt(i / instance.tilesPerRow);
                for (var j = 0; j < instance.tiles.length; j++) {
                    if (instance.tiles[j].root == root) {
                        var xofj = j % instance.tilesPerRow;
                        var yofj = parseInt(j / instance.tilesPerRow);
                        instance.tiles[j].position = instance.tiles[i].position + new Point(instance.tileWidth * (xofj - xofi), instance.tileWidth * (yofj - yofi));
                        flags[j] = 1;
                    }
                }
            }
        }
        for (var i = 0; i < instance.tiles.length; i++) {
            if (!flags[i]) {
                var pos = instance.tiles[i].position;
                if (pos.y < instance.tileWidth / 2) {
                    pos.y = instance.tileWidth / 2;
                }
                else if (pos.y > view.viewSize.height - instance.tileWidth / 2) {
                    pos.y = view.viewSize.height - instance.tileWidth / 2;
                }
                if (pos.x < instance.tileWidth / 2) {
                    pos.x = instance.tileWidth / 2;
                }
                else if (pos.x > view.viewSize.width - instance.tileWidth / 2) {
                    pos.x = view.viewSize.width - instance.tileWidth / 2;
                }
            }
        }
        instance.viewHeight = view.viewSize.height;
        instance.viewWidth = view.viewSize.width;
    }

    function endGame() {
        if (isCounting) {
            isCounting = false;
            timeCost = $('#showTime').text();
            oldText = $('#endShow').text();
            score = getScore(timeCost);
            document.getElementById("endDialog").setAttribute("style", "display: block;")
            // console.log(timeCost);
            $('#endShow').text(oldText + "\nUsing " + timeCost + "\nScore: " + score);
            clearTimeout(timeoutId); //清除指定id计时器
            document.getElementById("btnStartPause").disabled = true;
        }
    };

    function getScore(timeCost) {
        var hour = parseInt(timeCost.substr(0, 2));
        var mins = parseInt(timeCost.substr(5, 2));
        var secs = parseInt(timeCost.substr(10, 2));
        var number = instance.tilesPerColumn * instance.tilesPerRow;
        secs = hour * 3600 + mins * 60 + secs + hintCntTimes * 10 + magnetCntTimes * 20;
        console.log(secs, score);
        var score = 100 - 15 * Math.sqrt(secs) / number;
        // if(score < 0) score = Math.random() + 20;
        // if(game_mode == 1) {
        //     score *= 0.88;
        // }
        return score.toFixed(2);
    }

    this.getHint = function() {
        var flags = new Array(instance.tiles.length);
        var cnt = 0;
        var tileIndex = Math.floor(Math.random() * instance.tiles.length);
        while (true) {
            while (flags[tileIndex] || instance.tiles[tileIndex].opacity != 1) {
                tileIndex = Math.floor(Math.random() * instance.tiles.length);
            }
            var root = instance.tiles[tileIndex].root;
            var selectedX = tileIndex % instance.tilesPerRow;
            var selectedY = parseInt(tileIndex / instance.tilesPerRow);
            if (selectedX != 0 && instance.tiles[tileIndex - 1].opacity == 1) { // left
                var leftTile = instance.tiles[tileIndex - 1];
                if (leftTile.root != root) {
                    instance.hintA = new Array();
                    instance.hintB = new Array();
                    for (var i = 0; i < instance.tiles.length; i++) {
                        var temp = instance.tiles[i].root;
                        if (instance.tiles[i].opacity != 1) {
                            continue;
                        }
                        if (temp == root) {
                            instance.hintA.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                        else if (temp == leftTile.root) {
                            instance.hintB.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                    }
                    project.activeLayer.selected = false;
                    return true;
                }
            }
            if (selectedY != instance.tilesPerColumn - 1 && instance.tiles[tileIndex + instance.tilesPerRow].opacity == 1) { // bottom
                var bottomTile = instance.tiles[tileIndex + instance.tilesPerRow];
                if (bottomTile.root != root) {
                    instance.hintA = new Array();
                    instance.hintB = new Array();
                    for (var i = 0; i < instance.tiles.length; i++) {
                        var temp = instance.tiles[i].root;
                        if (instance.tiles[i].opacity != 1) {
                            continue;
                        }
                        if (temp == root) {
                            instance.hintA.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                        else if (temp == bottomTile.root) {
                            instance.hintB.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                    }
                    project.activeLayer.selected = false;
                    return true;
                }
            }
            if (selectedX != instance.tilesPerRow - 1 && instance.tiles[tileIndex + 1].opacity == 1) { // right
                var rightTile = instance.tiles[tileIndex + 1];
                if (rightTile.root != root) {
                    instance.hintA = new Array();
                    instance.hintB = new Array();
                    for (var i = 0; i < instance.tiles.length; i++) {
                        var temp = instance.tiles[i].root;
                        if (instance.tiles[i].opacity != 1) {
                            continue;
                        }
                        if (temp == root) {
                            instance.hintA.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                        else if (temp == rightTile.root) {
                            instance.hintB.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                    }
                    project.activeLayer.selected = false;
                    return true;
                }
            }
            if (selectedY != 0 && instance.tiles[tileIndex - instance.tilesPerRow].opacity == 1) { // top
                var topTile = instance.tiles[tileIndex - instance.tilesPerRow];
                if (topTile.root != root) {
                    instance.hintA = new Array();
                    instance.hintB = new Array();
                    for (var i = 0; i < instance.tiles.length; i++) {
                        var temp = instance.tiles[i].root;
                        if (instance.tiles[i].opacity != 1) {
                            continue;
                        }
                        if (temp == root) {
                            instance.hintA.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                        else if (temp == topTile.root) {
                            instance.hintB.push(i);
                            project.activeLayer.addChild(instance.tiles[i]);
                        }
                    }
                    project.activeLayer.selected = false;
                    return true;
                }
            }
            flags[tileIndex] = 1;
            cnt++;
            if (instance.borderOnly == 0) {
                if (cnt == instance.tiles.length) {
                    hint = 0;
                    break;
                }
            }
            else {
                if (cnt == instance.tilesPerColumn * 2 + instance.tilesPerRow * 2 - 4) {
                    hint = 0;
                    break;
                }
            }
            
        }
        return false;
    }

    this.rearrange = function() {
        var margin = instance.tileWidth * 0.2;
        var flags = new Array(instance.tiles.length);
        var cnt = 0, cntPos = 0;
        var perColumn = Math.floor(view.viewSize.height / (instance.tileWidth + margin));
        while (true) {
            var i = Math.floor(Math.random() * instance.tiles.length);
            if (flags[i] || instance.tiles[i].opacity != 1) {
                continue;
            }
            var root = instance.tiles[i].root;
            for (var j = 0; j < instance.tiles.length; j++) {
                if (j != i && instance.tiles[j].root == root) {
                    if (!flags[i]) {
                        flags[i] = 1;
                        cnt++;
                    }
                    if (!flags[j]) {
                        flags[j] = 1;
                        cnt++;
                    }
                }
            }
            if (!flags[i]) {
                var column = Math.floor(cntPos / perColumn);
                var row = cntPos % perColumn;
                if (column % 2 == 0) { // left side
                    var newPos = new Point(instance.tileWidth / 2 + Math.floor(column / 2) * (instance.tileWidth + margin), instance.tileWidth / 2 + row * (instance.tileWidth + margin));
                    moveTiles(instance.tiles[i], newPos - instance.tiles[i].position);
                    project.activeLayer.addChild(instance.tiles[i]);
                }
                else { // right side
                    var newPos = new Point(view.viewSize.width - (instance.tileWidth / 2 + Math.floor(column / 2) * (instance.tileWidth + margin)), instance.tileWidth / 2 + row * (instance.tileWidth + margin));
                    moveTiles(instance.tiles[i], newPos - instance.tiles[i].position);
                    project.activeLayer.addChild(instance.tiles[i]);
                }
                flags[i] = 1;
                cnt++;
                cntPos++;
            }
            if (instance.borderOnly == 0) {
                if (cnt == instance.tiles.length) {
                    project.activeLayer.selected = false;
                    break;
                }
            }
            else {
                if (cnt == instance.tilesPerColumn * 2 + instance.tilesPerRow * 2 - 4) {
                    project.activeLayer.selected = false;
                    break;
                }
            }
        }
    }

}
